package com.ahdms.es.engine;

import com.ahdms.es.bean.SealInfo;
import com.ahdms.es.bean.enums.ApiCode;
import com.ahdms.es.exceptions.*;
import com.ahdms.es.gm.constant.IKIObjectIdentifiers;
import com.ahdms.es.gm.v1.SESeal;
import com.ahdms.es.result.VerifyResult;
import com.ahdms.es.util.SM2Utils;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x509.Certificate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.text.ParseException;
import java.util.Date;
import java.util.Objects;

/**
 * @author qinxiang
 * @date 2021-04-13 15:22
 */
public abstract class SeSealVerifyEngine {
    protected static final Logger log = LoggerFactory.getLogger(SeSealVerifyEngine.class);

    public static SeSealVerifyEngine getInstance(byte[] sealData) throws VerifySealException {
        ASN1Sequence seq = ASN1Sequence.getInstance(sealData);
        if (seq.size() == 4) {
            return SeSealGBVerifyEngine.getInstance(sealData);
        } else {
            if (seq.size() != 2) {
                throw new VerifySealException(ApiCode.SEAL_VERSION_FAIL);
            }
            return SeSealGMVerifyEngine.getInstance(sealData);
        }
    }

    /**
     * 电子印章验证
     * @param sealData 电子印章数据结构
     * @return
     * @throws InvalidSealFormatException
     */
    public static VerifyResult<SealInfo> verify(byte[] sealData) {
        try {
            SeSealVerifyEngine sealVerifyEngine = SeSealVerifyEngine.getInstance(sealData);
            return sealVerifyEngine.verify();
        } catch (VerifySealException e) {
            return VerifyResult.error(e.getAlertAble());
        }
    }

    /**
     * 国密规范电子印章验证
     * @param seSeal 国密规范电子印章
     * @return
     * @throws InvalidSealFormatException
     */
    public static VerifyResult<SealInfo> verify(SESeal seSeal) {
        try {
            return SeSealGMVerifyEngine.getInstance(seSeal).verify();
        }catch (VerifySealException e){
            log.error(e.getMessage());
            return VerifyResult.error(e.getAlertAble());
        }
    }

    /**
     * 国标规范电子印章验证
     * @param seSeal 国标规范电子印章
     * @return
     * @throws InvalidSealFormatException
     */
    public static VerifyResult<SealInfo> verify(com.ahdms.es.gm.v4.SESeal seSeal) {
        try {
            return SeSealGBVerifyEngine.getInstance(seSeal).verify();
        }catch (VerifySealException e){
            return VerifyResult.error(e.getAlertAble());
        }
    }

    protected VerifyResult<SealInfo> verify(){
        try {
            verifySignData();
            verifyCert();
            verifyDate();
            return VerifyResult.success(getSealInfo());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (VerifyException e){
            e.printStackTrace();
            return VerifyResult.error(e.getAlertAble());
        }
        return VerifyResult.error(ApiCode.SEAL_FORMART_FAIL);
    }

    /**
     * 验证电子印章签名值是否正确
     * @throws IOException
     * @throws InvalidSignedValueException
     */
    public abstract void verifySignData() throws IOException, VerifySealException;

    /**
     * 验证制章人证书有效性
     * @throws
     */
    public void verifyCert() throws VerifyCertException {
        VerifyResult certVerifyResult = X509CertVerifyEngine.verifyX509Cert(getCert());
        if(!certVerifyResult.isResult()){
            log.error("制章人证书验证失败,"+certVerifyResult.getErrorMsg());
            throw new VerifyCertException(ApiCode.SEAL_CERT_Invalid);
        }
    }


    /**
     * 验证电子印章的有效期
     * @throws ParseException
     * @throws InvalidSealDateException
     */
    public abstract void verifyDate() throws ParseException, VerifySealException;

    public abstract Certificate getCert();

    protected void verifySignData(Certificate x509Cert, String algorithm, byte[] sourceData, byte[] signData) throws VerifySealException {
        if(Objects.equals(IKIObjectIdentifiers.sm2_with_sm3.getId(),algorithm)){
            boolean verifySignResult = SM2Utils.verifySign(x509Cert, sourceData, signData);
            if(!verifySignResult){
                throw new VerifySealException(ApiCode.SEAL_VERIFY_SIGN_FAIL);
            }
        }else{
            throw new VerifySealException(ApiCode.SEAL_SIGN_Algorithm_FAIL);
        }
    }

    protected void verifyDate(Date date,Date startDate,Date endDate) throws VerifySealException {
        if(date.before(startDate)){
            throw new VerifySealException(ApiCode.SEAL_DATE_Invalid);
        }
        if(date.after(endDate)){
            throw new VerifySealException(ApiCode.SEAL_DATE_Expired);
        }
    }

    public abstract SealInfo getSealInfo();

}
